#pragma once

#include <fstream>
#include <sstream>
#include <vector>
#include <map>

#include "IniError.hpp"
#include "IniExcep.hpp"

namespace header
{
namespace ini
{
namespace details
{

template <typename T>
std::string mark(const T& t)
{
    return " [" + std::to_string(t) + "] ";
}

template <typename T>
std::string mark(char c)
{
    return " [" + std::string(c) + "] ";
}

template <typename T>
std::string mark(const std::string& t)
{
    return " [" + t + "] ";
}

}   // namespace details

class Value
{

};


using Section = std::map<std::string, std::string>;
using IniConfig = std::map<std::string, Section>;

class IniParse
{
public:
    static const char comment_begin = '#';
    static const char section_begin = '[';
    static const char section_end   = ']';
    static const char assignment    = '=';

public:
    IniParse() = default;
    ~IniParse() = default;

public:
    IniConfig get_config() const { return _config; }
    Section   get_section(const std::string& section)
    {
        if (!_config.count(section)) throw IniExcep{"not find section:" + details::mark(section) };
        return _config[section];
    }

public:
    void parse(const std::string& file_path)
    {
        std::ifstream ifs(file_path);
        if (!ifs) throw IniExcep{"not open file:" + details::mark(file_path)};
        _file = file_path;
        while (true) {
            char c = ifs.peek();
            if (ifs.eof()) return;
            switch (c) {
                case comment_begin:
                {
                    deal_comment(ifs);
                    break;
                }
                case section_begin:
                {

                }
            }
        }
    }

private:    /* helper function */
    bool is_id_ch(char c)
    {
        return isalnum(c) || c == '_';
    }

    std::string get_identifier(std::istream& is)
    {
        std::string res;
        if (!is_id_ch(is.peek())) return res;
        while (true) {
            char c = is.get();
            if (is.eof()) break;
            if (!is_id_ch(c)) {
                is.putback(c);
                break;
            }
            res.push_back(c);
        }
        return res;
    }

private:   
    void clear_mess(std::istream& is)
    {
        std::string s;
        std::getline(is, s);
        ++_line; 
        _col = 0;
    }

    void deal_comment(std::istream& is)
    {
        std::string comment;
        std::getline(is, comment);
        ++_line;
        _col = 0;
    }

    void get_section_name(std::istream& is)
    {
        char c = is.get();
        ++_col;
        std::string section;
        while (true) {
            c = is.peek();
            if (c == section_end) {
                is.get();
                break;
            }
            else if (is.eof() || c == '\n') {
                std::string end{section_end};
                _errors.emplace_back(IniError{_line, _col, "lack of section-end: '" + end + "'"});
                if (c == '\n') {
                    ++_line;
                    _col = 0;
                }
                return;
            } 
            section.push_back(is.get());
            ++_col;
        } 
        if (section.empty()) 
            _errors.emplace_back(IniError{_line, _col, "lack of section name"});
        _config[section] = {};
    }

    // if (key == ""): 说明读取失败
    void get_key_value(std::istream& is, std::string& key, std::string& value)
    {
        key = get_identifier(is);
        _col += key.size();
        char c = is.get();
        ++_col;
        while (isspace(c)) {
            if (is.eof()) break;
            if (c == '\n') {
                is.putback(c);
                break;
            }
            c = is.get();
            ++_col;
        }
        if (c != assignment) {
            std::string assign = std::string{assignment};
            _errors.emplace_back(IniError{_line, _col, "lack of assgnment: '" + assign + "'"});
            clear_mess(is);
            key = "";
            return;
        }
        c = is.get();
        while (isspace(c)) {
            
        }
    }

private:
    IniConfig             _config;
    std::string           _file = "";
    line_t                _line = 0;
    col_t                 _col  = 0;
    std::vector<IniError> _errors;
};

}   // namespace ini
}   // namespace header
